#include "input.h"
#include "defines/primitive.h"
#include "src/cuboidPrimitive.h"
#include "src/spherePrimitive.h"
#include "src/cylinderPrimitive.h"
#include "src/conePrimitive.h"
#include "src/combinedObject.h"

#include <QDebug>
#include <QTextStream>
#include <QStringList>

Input::Input(){
    file = NULL;
}

Input::~Input(){
}

std::vector<Object *> Input::parseObjects(QFile * _file){
    file = _file;

    file->open(QFile::ReadOnly);

	char objectType;

	std::vector<Object *> objects;

	QTextStream stream(file);

	while(stream >> objectType, !stream.atEnd()) {
	    int id, type, parentId;
	    double ambientR, ambientG, ambientB, diffuseR, diffuseG, diffuseB, specularR, specularG, specularB;
	    Vector3D center, relativePosition;
	    Matrix m(4,4);
	    Matrix mInverse(4,4);

        Object *obj;

        stream >> id >> type >> parentId >> ambientR >> ambientG >> ambientB >> diffuseR >> diffuseG >> diffuseB >> specularR >> specularG >> specularB >>
                  center.x >> center.y >> center.z >> relativePosition.x >> relativePosition.y >> relativePosition.z >>
                  m[0][0] >> m[0][1] >> m[0][2] >> m[0][3] >> m[1][0] >> m[1][1] >> m[1][2] >> m[1][3] >>  m[2][0] >> m[2][1] >> m[2][2] >> m[2][3] >>  m[3][0] >> m[3][1] >> m[3][2] >> m[3][3] >>
                  mInverse[0][0] >> mInverse[0][1] >> mInverse[0][2] >> mInverse[0][3] >> mInverse[1][0] >> mInverse[1][1] >> mInverse[1][2] >> mInverse[1][3] >>  mInverse[2][0] >> mInverse[2][1] >> mInverse[2][2] >> mInverse[2][3] >>  mInverse[3][0] >> mInverse[3][1] >> mInverse[3][2] >> mInverse[3][3];

//        qDebug() <<objectType;
//        std::cout <<"<<<< Foi lido!" << "\n\tID: " << id <<
//                    "\n\tTIPO: " << type << "\n\tPARENTE: " <<parentId <<
//                    "\n\tCOR (" <<colorR << ", " << colorG << ", " << colorB << ", " << colorA  <<")"<<std::endl;

		if(objectType == 'p') {
            switch(type){
                case CUBOID:{
                    double minX, minY, minZ, maxX, maxY, maxZ;
                    stream >> minX >> minY >> minZ >> maxX >> maxY >> maxZ      >> objectType;
                    obj = new CuboidPrimitive( Vector3D(minX, minY, minZ),
                                               Vector3D(maxX, maxY, maxZ),
                                               center, //relativePosition,
                                               Material(ambientR, ambientG, ambientB, diffuseR, diffuseG, diffuseB, specularR, specularG, specularB),
                                               m, mInverse,
                                               id );


//        std::cout <<"\tCUBO: \n\t\tMin (" << minX <<  ", " <<minY <<  ", " <<minZ << ")"
//                    "\n\t\tMax (" << maxX <<  ", " <<maxY <<  ", " <<maxZ << ")          " << "!"<<std::endl <<std::endl;
                    break;
                }
                case SPHERE:{
                    double radius;
                    stream >> radius      >> objectType;
                    obj = new SpherePrimitive( center, //relativePosition,
                                               radius,
                                               Material(ambientR, ambientG, ambientB, diffuseR, diffuseG, diffuseB, specularR, specularG, specularB),
                                               m, mInverse,
                                               id );

//        std::cout <<"\tEsfera: \n\t\tCentro (" << centerX <<  ", " <<centerY <<  ", " <<centerZ << ")"
//                    "\n\t\tRaio: " << radius <<std::endl <<std::endl;
                    break;
                }
                case CYLINDER:{
                    double radius, height;
                    stream >> radius >> height      >> objectType;
                    obj = new CylinderPrimitive( center, //relativePosition,
                                               radius,
                                               height,
                                               Material(ambientR, ambientG, ambientB, diffuseR, diffuseG, diffuseB, specularR, specularG, specularB),
                                               m, mInverse,
                                               id );

//        std::cout <<"\tCilindro: \n\t\tCentro (" << centerX <<  ", " <<centerY <<  ", " <<centerZ << ")"
//                    "\n\t\tRaio: " << radius <<"\n\t\tAltura: " <<height <<std::endl <<std::endl;
                    break;
                }
                case CONE:{
                    double radius, height;
                    stream >> radius >> height      >> objectType;
                    obj = new ConePrimitive( center, //relativePosition,
                                               radius,
                                               height,
                                               Material(ambientR, ambientG, ambientB, diffuseR, diffuseG, diffuseB, specularR, specularG, specularB),
                                               m, mInverse,
                                               id );

//        std::cout <<"\tCone: \n\t\tCentro (" << centerX <<  ", " <<centerY <<  ", " <<centerZ << ")"
//                    "\n\t\tRaio: " << radius <<"\n\t\tAltura: " <<height <<std::endl <<std::endl;
                    break;
                }
            }
		} else if(objectType == 'o') {
		    stream      >> objectType;
            obj = new CombinedObject( type, center, //relativePosition,
                                      Material(ambientR, ambientG, ambientB, diffuseR, diffuseG, diffuseB, specularR, specularG, specularB),
                                      m, mInverse,
                                      id );

//        std::cout <<"\tCombinação!" <<std::endl <<std::endl;
		}

        if(parentId == -1){
            objects.push_back(obj);
        }else{
            bool finded = false;
            for(unsigned int i = 0; i < objects.size(); i++){
                CombinedObject *parent = (CombinedObject *)objects.at(i);
                if(parent->getId() == parentId){
                    finded = true;
                    parent->addObject(obj);
                }
            }
            if(!finded) std::cout <<"Não foi achado o pai " << parentId << " do objeto " << id <<std::endl;
        }

	}

	file->close();
	return objects;

}

